#include <iostream>
#include <vector>

#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/algorithm/string.hpp>

#include "Session.hpp"
#include "Cryptography.hpp"

#define LoginID "admin"
#define LoginPW "123456"

#define CheckLoginPacketLength(length) (length > 30 && length < 256)
#define CheckLogin(username, password) (username == LoginID && password == LoginPW)

Cryptography cryptography;

std::string response[2] = {
	cryptography.EncryptLoginPacket("NsTeST 12345 127.0.0.1:4010:0:1.1.( C ) ( R ) ( A ) ( S ) ( H ) 127.0.0.1:4010:0:1.2.( C ) ( R ) ( A ) ( S ) ( H )"),
	cryptography.EncryptLoginPacket("fail Check your ID and Password!!")
};

Session::Session(boost::asio::ip::tcp::socket socket)
	: socket(std::move(socket)), timer(socket.get_io_service())
{
}

void Session::Begin()
{
	std::cout << "New session initialized !" << std::endl;
	Read();
}

void Session::Read()
{
	timer.expires_from_now(boost::posix_time::seconds(5));
	timer.async_wait(boost::bind(&Session::TimerHandler, shared_from_this(), boost::asio::placeholders::error));
	boost::asio::async_read_until(socket, buf, 0xD8, boost::bind(&Session::ReadHandler, shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
}

void Session::ReadHandler(const boost::system::error_code &error, std::size_t bytes_transferred)
{
	timer.cancel();

	if (!error && CheckLoginPacketLength(bytes_transferred))
	{
		boost::asio::streambuf::const_buffers_type buffer = buf.data();
		std::string packet = cryptography.DecryptLoginPacket(std::string(boost::asio::buffers_begin(buffer), boost::asio::buffers_end(buffer)), bytes_transferred);

		boost::algorithm::split(data, packet, std::bind2nd(std::equal_to<char>(), (' ')));

		if (CheckLogin(data[2], cryptography.DecryptLoginPassword(data[3])))
		{
			Write(response[0] += 0x19); // Server/Channel Selection
		}
		else
		{
			Write(response[1] += 0x19); // Check your ID and Password!!
		}
	}
	else
	{
		socket.close();
	}
}

void Session::Write(const std::string packet)
{
	boost::asio::async_write(socket, boost::asio::buffer(packet), boost::bind(&Session::WriteHandler, shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
}

void Session::WriteHandler(const boost::system::error_code &error, std::size_t bytes_transferred)
{
	if (error)
	{
		socket.close();
	}
}

void Session::TimerHandler(const boost::system::error_code &error)
{
	socket.close();
}